package com.seguriapp.dao;

import static org.hibernate.criterion.Restrictions.between;
import static org.hibernate.criterion.Restrictions.eq;

import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.criterion.Disjunction;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.springframework.stereotype.Repository;

import com.seguriapp.base.GenericDaoImpl;
import com.seguriapp.entities.SeguriAppEntity;
import com.seguriapp.entities.UserSeguriAppEntity;
import com.seguriapp.util.SearchFilterEntity;

/**
 * Data access object for {@link SeguriAppEntity} entities using Hibernate.
 * 
 * @author Tania Gonzales (taniadgv@gmail.com)
 */
@Repository
public class EntityDaoImpl extends GenericDaoImpl<SeguriAppEntity> implements EntityDao {

  private static final String LONGITUDE = "longitude";
  private static final String LATITUDE = "latitude";
  private static final String ACTIVE = "active";

  public EntityDaoImpl() {
    super(SeguriAppEntity.class);
  }

  @SuppressWarnings("unchecked")
  public List<SeguriAppEntity> listEntitiesByUbication(final double latitude, final double longitude, final double radio) {
    return getSession().createCriteria(SeguriAppEntity.class).add(between(LATITUDE, latitude - radio, latitude + radio))
        .add(between(LONGITUDE, longitude - radio, longitude + radio)).add(eq(ACTIVE, true)).list();
  }

  @SuppressWarnings("unchecked")
  public List<SeguriAppEntity> listEntitiesByCategories(final List<Long> categoriesIdsList, final Long userId) {
    final Criteria criteria = getSession().createCriteria(UserSeguriAppEntity.class);
    if (categoriesIdsList != null) {
      criteria.createAlias("entity", "e");
      final Disjunction disjunction = Restrictions.disjunction();
      for (final Long crimeCategoryId : categoriesIdsList) {
        disjunction.add(eq("e.type.id", crimeCategoryId));
      }
      criteria.add(disjunction);
    }
    if (userId != null) {
      criteria.add(eq("user.id", userId));
    }
    criteria.add(eq("e." + ACTIVE, true)).setProjection(Projections.distinct(Projections.property("entity")));
    return criteria.list();
  }

  @SuppressWarnings("unchecked")
  public List<SeguriAppEntity> listEntitiesByCategories(List<Long> categoriesIdsList) {
    final Criteria criteria = getSession().createCriteria(SeguriAppEntity.class);
    if (categoriesIdsList != null) {
      final Disjunction disjunction = Restrictions.disjunction();
      for (final Long entityTypeId : categoriesIdsList) {
        disjunction.add(eq("type.id", entityTypeId));
      }
      criteria.add(disjunction);
    }
    return criteria.add(eq(ACTIVE, true)).list();
  }

  @SuppressWarnings("unchecked")
  public List<SeguriAppEntity> listEntities(final SearchFilterEntity searchFilter) {
    final Criteria criteria = setCriteriaForQuery(searchFilter);
    if (searchFilter.getColumnSort() != null) {
      if (searchFilter.isAscending()) {
        criteria.addOrder(Order.asc(searchFilter.getColumnSort()));
      } else {
        criteria.addOrder(Order.desc(searchFilter.getColumnSort()));
      }
    } else {
      criteria.addOrder(Order.desc("type.id"));
    }
    if (searchFilter.isAllResults()) {
      return criteria.add(eq(ACTIVE, true)).list();
    } else {
      return criteria.add(eq(ACTIVE, true)).setMaxResults(searchFilter.getPageSize())
          .setFirstResult((searchFilter.getPage() - 1) * searchFilter.getPageSize()).list();
    }
  }

  public long getEntitiesTotalPages(final SearchFilterEntity searchFilter) {
    final Criteria criteria = setCriteriaForQuery(searchFilter);
    criteria.setProjection(Projections.rowCount());
    final long total = Long.valueOf(criteria.uniqueResult().toString());
    long pages = 0;
    if (searchFilter.getPageSize() != 0) {
      pages = total / searchFilter.getPageSize();
      if (total % searchFilter.getPageSize() > 0) {
        pages++;
      }
    }
    return pages;
  }

  public SeguriAppEntity findPoliceStationByDistrict(final Long districtId) {
    final Criteria criteria = getSession().createCriteria(SeguriAppEntity.class);
    criteria.add(eq("district.id", districtId)).add(eq("type.id", Long.valueOf(1)));
    return (SeguriAppEntity) criteria.add(eq(ACTIVE, true)).uniqueResult();
  }

  private Criteria setCriteriaForQuery(final SearchFilterEntity searchFilter) {
    final Criteria criteria = getSession().createCriteria(SeguriAppEntity.class);
    // by entityTypeId
    if (searchFilter.getEntityTypeId() != null) {
      criteria.add(eq("type.id", searchFilter.getEntityTypeId()));
    }
    // by districts
    if (searchFilter.getIdsList() != null && !searchFilter.getIdsList().isEmpty()) {
      final Disjunction disjunction = Restrictions.disjunction();
      for (final Long entityId : searchFilter.getIdsList()) {
        disjunction.add(eq("district.id", entityId));
      }
      criteria.add(disjunction);
    }
    return criteria;
  }

}
